﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Library
{
	public class Sprite
	{
		#region protected fields
		//Used to load content
		protected ContentManager _contentManager;

		//Used for drawing the sprite
		protected SpriteBatch _spriteBatch;

		//Used for loading the texture
		protected string _texturePath;

		//The texture of the sprite
		protected Texture2D _texture;

		//Used to check if the sprite is hidden
		protected bool isHidden = false;

		//Used for drawing the sprite in the right depth
		protected float _depth = 0.0f;

		//Used for fading the sprite in or out
		private bool _isFadingIn = false;
		private bool _isFadingOut = false;
		private float _fadeDelay = 0.35f;
		private double _fadeTimer = 0.0f;
		private float _fadeSmoothness = 1.0f;
		public float _alphaLevel = 0.0f;

		private bool usesRotation = false;

		//Used for drawing rotating/scaled sprites - Default is x=width/2 and y=height/2
		private Vector2 _origin;

		//Used for scaling the sprite
		private float _scale = 1;

		//Used to check if the sprite is animated
		private bool _isAnimated = false;

		//Used for animating the sprite
		private List<SpriteState> _animationStates = new List<SpriteState>();
		private SpriteState _currentState;
		private int previousCurrentFrame = 0;
		private int animationCycles = 0;
		public int AnimationCycles { get { return animationCycles; } }
		#endregion

		#region Public fields

		//White is default
		public Color color = Color.White;


		//position of the sprite
		public Vector2 position;

		//Rotation of the sprite
		public float rotation = 0f;

		//Rotation speed of the sprite
		public float rotationSpeed = 0f;

		private int _width = 0;
		public int Width { get { return _width; } }

		private int _height = 0;
		public int Height { get { return _height; } }

		/// <summary>
		/// SpriteEffect of the sprite. Can be used to flipping the sprite - Default is no effects
		/// </summary>
		public SpriteEffects spriteEffects = SpriteEffects.None;

		public Rectangle CollisionRectangle { get { return new Rectangle((int)position.X, (int)position.Y, _width, _height); } }
		#endregion

		#region Constructors
		public Sprite(string texturePath,float depth)
		{
			_texturePath = texturePath;
			position = Vector2.Zero;
			_depth = depth;
		}
		public Sprite(string texturePath, Vector2 position, float depth)
		{
			_texturePath = texturePath;
			this.position = position;
			_depth = depth;
		}
		public Sprite(string texturePath, int xCoordinate, int yCoordinate, float depth)
		{
			_texturePath = texturePath;
			this.position = new Vector2(xCoordinate, yCoordinate);
			_depth = depth;
		}

		//Use these for animated sprites and spritesheets
		public Sprite(string texturePath, int width, int height, bool animated, float depth)
		{
			_texturePath = texturePath;
			position = Vector2.Zero;
			_width = width;
			_height = height;
			_isAnimated = animated;
			_depth = depth;
		}
		public Sprite(string texturePath, Vector2 position, int width, int height, bool animated, float depth)
		{
			_texturePath = texturePath;
			this.position = position;
			_width = width;
			_height = height;
			_isAnimated = animated;
			_depth = depth;
		}
		public Sprite(string texturePath, int xCoordinate, int yCoordinate, int width, int height, bool animated, float depth)
		{
			_texturePath = texturePath;
			this.position = new Vector2(xCoordinate, yCoordinate);
			_width = width;
			_height = height;
			_isAnimated = animated;
			_depth = depth;
		}
		#endregion

		#region Loading and drawing
		public virtual void LoadContent(ContentManager contentManager, SpriteBatch spriteBatch)
		{
			_contentManager = contentManager;
			_spriteBatch = spriteBatch;
			_texture = _contentManager.Load<Texture2D>(_texturePath);
			if (_width == 0 && _height == 0) { _width = _texture.Width; _height = _texture.Height; }
			if (usesRotation)
			{
				_origin = new Vector2(_width / 2, _height / 2);
			}
			InitiateAnimationStates();
		}
		public virtual void Draw(GameTime gameTime)
		{
			if (!isHidden)
			{

				if(_isAnimated && _currentState != null)
				{
					_currentState.UpdateFrame((float)gameTime.ElapsedGameTime.TotalSeconds);

					_spriteBatch.Draw(_texture, position, _currentState.GetCurrentFrameRectangle(), Color.Lerp(color, Color.White, _alphaLevel / 255), rotation, _origin, _scale, spriteEffects, _depth);
					rotation += rotationSpeed;
					if (previousCurrentFrame != _currentState.currentFrame)
					{
						animationCycles++;
						previousCurrentFrame = _currentState.currentFrame;
					}
				}
				else
				{
					_spriteBatch.Draw(_texture, position, null, Color.Lerp(color, Color.White, _alphaLevel / 255), rotation, _origin, _scale, spriteEffects, _depth);
					rotation += rotationSpeed;
				}
				if (_isFadingIn)
					UpdateFadeIn(gameTime);
				else if (_isFadingOut)
					UpdateFadeOut(gameTime);
			}
		}
		protected virtual void InitiateAnimationStates() { }
		protected Rectangle GetRectangle(int width, int height, int border, int xPosition, int yPosition)
		{
			return new Rectangle(xPosition * width + border * (xPosition + 1), yPosition * height + border * (yPosition + 1), width, height);
		}
		private void UpdateFadeIn(GameTime gameTime)
		{
			_fadeTimer -= gameTime.ElapsedGameTime.TotalSeconds;
			if (_fadeTimer <= 0)
			{
				_fadeTimer = _fadeDelay;
				_alphaLevel += _fadeSmoothness;
				if (_alphaLevel >= 255)
				{
					_isFadingIn = false;
				}
			}
		}
		private void UpdateFadeOut(GameTime gameTime)
		{
			_fadeTimer -= gameTime.ElapsedGameTime.TotalSeconds;
			if (_fadeTimer <= 0)
			{
				_fadeTimer = _fadeDelay;
				_alphaLevel -= _fadeSmoothness;
				if (_alphaLevel <= 0)
				{
					_isFadingOut = false;
				}
			}
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Hides the sprite
		/// </summary>
		public void Hide()
		{
			isHidden = true;
		}

		/// <summary>
		/// Makes the sprite visible
		/// </summary>
		public void Show()
		{
			isHidden = false;
		}

		//Fix fade in and out
		/// <summary>
		/// Used for fading the sprite in
		/// </summary>
		public void FadeIn(float fadeTime, float fadeSmoothness)
		{
			if (_isFadingIn == false)
			{
				_isFadingIn = true;
				_fadeSmoothness = fadeSmoothness;
				_fadeDelay = fadeSmoothness / fadeTime;
				_alphaLevel = 0;
			}
		}

		/// <summary>
		/// Used for fading the sprite out
		/// </summary>
		public void FadeOut(float fadeTime, float fadeSmoothness)
		{
			if (_isFadingOut == false)
			{
				_isFadingOut = true;
				_fadeSmoothness = fadeSmoothness/fadeTime;
				_fadeDelay = _fadeSmoothness;
				_alphaLevel = 255;
			}
		}

		/// <summary>
		/// Used for setting the origin of the sprite. 
		/// This is used when scaling or rotating the sprite
		/// </summary>
		public void SetOrigin(int x, int y)
		{
			_origin = new Vector2(x, y);
		}

		/// <summary>
		/// Set the scale of the sprite. 1 is the original size.
		/// </summary>
		public void SetScale(float scale)
		{
			_scale = scale;
		}

		/// <summary>
		/// Checks if the sprite is colliding with another sprite, returns true if this is the case and return false if not.
		/// Warning: Very simple collision check
		/// </summary>
		public bool CheckRectangluarCollision(Sprite sprite)
		{
			if (sprite.CollisionRectangle.Intersects(this.CollisionRectangle))
			{
				return true;
			}
			return false;
		}
		/// <summary>
		/// Checks if the sprite is colliding with rectangle, returns true if this is the case and return false if not.
		/// Warning: Very simple collision check
		/// </summary>
		public bool CheckRectangluarCollision(Rectangle rectangle)
		{
			if (rectangle.Intersects((CollisionRectangle)))
			{
				return true;
			}
			return false;
		}
		public void AddAnimationState(SpriteState state)
		{
			_animationStates.Add(state);
		}
		public void SetCurrentAnimationState(string name)
		{
			foreach(SpriteState state in _animationStates )
			{
				if (state.name == name)
				{
					_currentState = state;
					_currentState.currentFrame = 0;
					animationCycles = 0;
					previousCurrentFrame = 0;
					break;
				}
			}
		}
		#endregion
	}
}
